---
title: "Pattern Matching / Destructuring"
description: "Pattern matching and destructuring complex data structures in ReScript"
canonical: "/docs/manual/pattern-matching-destructuring"
section: "Language Features"
order: 16
---

# Pattern Matching / Destructuring

One of ReScript's **best** feature is our pattern matching. Pattern matching combines 3 brilliant features into one:

- Destructuring.
- `switch` based on shape of data.
- Exhaustiveness check.

We'll dive into each aspect below.

## Destructuring

Even JavaScript has destructuring, which is "opening up" a data structure to extract the parts we want and assign variable names to them:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
let coordinates = (10, 20, 30)
let (x, _, _) = coordinates
Console.log(x) // 10
```

```js
var coordinates = [10, 20, 30];
var x = 10;
console.log(10);
```

</CodeTab>

Destructuring works with most built-in data structures:

<CodeTab labels={["ReScript", "JS Output"]}>

```res
// Record
type student = {name: string, age: int}
let student1 = {name: "John", age: 10}
let {name} = student1 // "John" assigned to `name`

// Variant
type result =
  | Success(string)
let myResult = Success("You did it!")
let Success(message) = myResult // "You did it!" assigned to `message`
```

```js
var student1 = {
  name: "John",
  age: 10,
};
var name = "John";

var myResult = /* Success */ {
  _0: "You did it!",
};
var message = "You did it!";

var myArray = [1, 2, 3];
if (myArray.length !== 2) {
  throw {
    RE_EXN_ID: "Match_failure",
    _1: ["playground.res", 14, 4],
    Error: new Error(),
  };
}
var item1 = myArray[0];
var item2 = myArray[1];

var myList = {
  hd: 1,
  tl: {
    hd: 2,
    tl: {
      hd: 3,
      tl: /* [] */ 0,
    },
  },
};
// ...
```

</CodeTab>

You can also use destructuring anywhere you'd usually put a binding:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
type result =
  | Success(string)
let displayMessage = (Success(m)) => {
  // we've directly extracted the success message
  // string by destructuring the parameter
  Console.log(m)
}
displayMessage(Success("You did it!"))
```

```js
function displayMessage(m) {
  console.log(m._0);
}

displayMessage(
  /* Success */ {
    _0: "You did it!",
  },
);
```

</CodeTab>

For a record, you can rename the field while destructuring:

<CodeTab labels={["ReScript", "JS Output"]}>

```res
let {name: n} = student1 // "John" assigned to `n`
```

```js
var n = "John";
```

</CodeTab>

You _can_ in theory destructure array and list at the top level too:

```res
let myArray = [1, 2, 3]
let [item1, item2, _] = myArray
// 1 assigned to `item1`, 2 assigned to `item2`, 3rd item ignored

let myList = list{1, 2, 3}
let list{head, ...tail} = myList
// 1 assigned to `head`, `list{2, 3}` assigned to tail
```

But the array example is **highly disrecommended** (use tuple instead) and the list example will error on you. They're only there for completeness' sake. As you'll see below, the proper way of using destructuring array and list is using `switch`.

## `switch` Based on Shape of Data

While the destructuring aspect of pattern matching is nice, it doesn't really change the way you think about structuring your code. One paradigm-changing way of thinking about your code is to execute some code based on the shape of the data.

Consider a variant:

<CodeTab labels={["ReScript", "JS Output"]}>

```res prelude
type payload =
  | BadResult(int)
  | GoodResult(string)
  | NoResult
```

```js
// Empty output
```

</CodeTab>

We'd like to handle each of the 3 cases differently. For example, print a success message if the value is `GoodResult(...)`, do something else when the value is `NoResult`, etc.

In other languages, you'd end up with a series of if-elses that are hard to read and error-prone. In ReScript, you can instead use the supercharged `switch` pattern matching facility to destructure the value while calling the right code based on what you destructured:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
let data = GoodResult("Product shipped!")
switch data {
| GoodResult(theMessage) =>
  Console.log("Success! " ++ theMessage)
| BadResult(errorCode) =>
  Console.log("Something's wrong. The error code is: " ++ Int.toString(errorCode))
| NoResult =>
  Console.log("Bah.")
}
```

```js
var data = {
  TAG: "GoodResult",
  _0: "Product shipped!",
};

if (typeof data !== "object") {
  console.log("Bah.");
} else if (data.TAG === "BadResult") {
  console.log(
    "Something's wrong. The error code is: " + "Product shipped!".toString(),
  );
} else {
  console.log("Success! Product shipped!");
}
```

</CodeTab>

In this case, `message` will have the value `"Success! Product shipped!"`.

Suddenly, your if-elses that messily checks some structure of the value got turned into a clean, compiler-verified, linear list of code to execute based on exactly the shape of the value.

### Complex Examples

Here's a real-world scenario that'd be a headache to code in other languages. Given this data structure:

<CodeTab labels={["ReScript", "JS Output"]}>

```res prelude
type status = Vacations(int) | Sabbatical(int) | Sick | Present
type reportCard = {passing: bool, gpa: float}
type student = {name: string, status: status, reportCard: reportCard}
type person =
  | Teacher({name: string, age: int})
  | Student(student)
```

```js
// Empty output
```

</CodeTab>

Imagine this requirement:

- Informally greet a person who's a teacher and if his name is Mary or Joe.
- Greet other teachers formally.
- If the person's a student, congratulate him/her score if they passed the semester.
- If the student has a gpa of 0 and is on vacations or sabbatical, display a different message.
- A catch-all message for a student.

ReScript can do this easily!

<CodeTab labels={["ReScript", "JS Output"]}>

```res prelude
let person1 = Teacher({name: "Jane", age: 35})

let message = switch person1 {
| Teacher({name: "Mary" | "Joe"}) =>
  `Hey, still going to the party on Saturday?`
| Teacher({name}) =>
  // this is matched only if `name` isn't "Mary" or "Joe"
  `Hello ${name}.`
| Student({name, reportCard: {passing: true, gpa}}) =>
  `Congrats ${name}, nice GPA of ${Float.toString(gpa)} you got there!`
| Student({
    reportCard: {gpa: 0.0},
    status: Vacations(daysLeft) | Sabbatical(daysLeft)
  }) =>
  `Come back in ${Int.toString(daysLeft)} days!`
| Student({status: Sick}) =>
  `How are you feeling?`
| Student({name}) =>
  `Good luck next semester ${name}!`
}
```

```js
var person1 = {
  TAG: "Teacher",
  name: "Jane",
  age: 35,
};

var message;

if (person1.TAG === "Teacher") {
  message = "Hello Jane.";
} else {
  var match = "Jane";
  var match$1 = match.status;
  var name = match.name;
  var match$2 = match.reportCard;
  if (match$2.passing) {
    message =
      "Congrats " +
      name +
      ", nice GPA of " +
      match$2.gpa.toString() +
      " you got there!";
  } else {
    var exit = 0;
    if (typeof match$1 !== "object") {
      message =
        match$1 === "Sick"
          ? "How are you feeling?"
          : "Good luck next semester " + name + "!";
    } else {
      exit = 1;
    }
    if (exit === 1) {
      message =
        match.reportCard.gpa !== 0.0
          ? "Good luck next semester " + name + "!"
          : "Come back in " + match$1._0.toString() + " days!";
    }
  }
}
```

</CodeTab>

**Note** how we've:

- drilled deep down into the value concisely
- using a **nested pattern check** `"Mary" | "Joe"` and `Vacations | Sabbatical`
- while extracting the `daysLeft` number from the latter case
- and assigned the greeting to the binding `message`.

Here's another example of pattern matching, this time on an inline tuple.

<CodeTab labels={["ReScript", "JS Output"]}>

```res
type animal = Dog | Cat | Bird
let categoryId = switch (isBig, myAnimal) {
| (true, Dog) => 1
| (true, Cat) => 2
| (true, Bird) => 3
| (false, Dog | Cat) => 4
| (false, Bird) => 5
}
```

```js
var categoryId = isBig ? (myAnimal + 1) | 0 : myAnimal >= 2 ? 5 : 4;
```

</CodeTab>

**Note** how pattern matching on a tuple is equivalent to a 2D table:

| isBig \ myAnimal | Dog | Cat | Bird |
| ---------------- | --- | --- | ---- |
| true             | 1   | 2   | 3    |
| false            | 4   | 4   | 5    |

### Fall-Through Patterns

The nested pattern check, demonstrated in the earlier `person` example, also works at the top level of a `switch`:

<CodeTab labels={["ReScript", "JS Output"]}>

```res prelude
let myStatus = Vacations(10)

switch myStatus {
| Vacations(days)
| Sabbatical(days) => Console.log(`Come back in ${Int.toString(days)} days!`)
| Sick
| Present => Console.log("Hey! How are you?")
}
```

```js
var myStatus = {
  TAG: /* Vacations */ 0,
  _0: 10,
};

if (typeof myStatus === "number") {
  console.log("Hey! How are you?");
} else {
  console.log("Come back in " + (10).toString() + " days!");
}
```

</CodeTab>

Having multiple cases fall into the same handling can clean up certain types of logic.

### Ignore Part of a Value

If you have a value like `Teacher(payload)` where you just want to pattern match on the `Teacher` part and ignore the `payload` completely, you can use the `_` wildcard like this:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
switch person1 {
| Teacher(_) => Console.log("Hi teacher")
| Student(_) => Console.log("Hey student")
}
```

```js
if (person1.TAG === "Teacher") {
  console.log("Hi teacher");
} else {
  console.log("Hey student");
}
```

</CodeTab>

`_` also works at the top level of the `switch`, serving as a catch-all condition:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
switch myStatus {
| Vacations(_) => Console.log("Have fun!")
| _ => Console.log("Ok.")
}
```

```js
if (typeof myStatus !== "object" || myStatus.TAG !== "Vacations") {
  console.log("Ok.");
} else {
  console.log("Have fun!");
}
```

</CodeTab>

**Do not** abuse a top-level catch-all condition. Instead, prefer writing out all the cases:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
switch myStatus {
| Vacations(_) => Console.log("Have fun!")
| Sabbatical(_) | Sick | Present => Console.log("Ok.")
}
```

```js
if (typeof myStatus !== "object" || myStatus.TAG !== "Vacations") {
  console.log("Ok.");
} else {
  console.log("Have fun!");
}
```

</CodeTab>

Slightly more verbose, but a one-time writing effort. This helps when you add a new variant case e.g. `Quarantined` to the `status` type and need to update the places that pattern match on it. A top-level wildcard here would have accidentally and silently continued working, potentially causing bugs.

### If Clause

Sometime, you want to check more than the shape of a value. You want to also run some arbitrary check on it. You might be tempted to write this:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
switch person1 {
| Teacher(_) => () // do nothing
| Student({reportCard: {gpa}}) =>
  if gpa < 0.5 {
    Console.log("What's happening")
  } else {
    Console.log("Heyo")
  }
}
```

```js
if (person1.TAG !== "Teacher") {
  if ("Jane".reportCard.gpa < 0.5) {
    console.log("What's happening");
  } else {
    console.log("Heyo");
  }
}
```

</CodeTab>

`switch` patterns support a shortcut for the arbitrary `if` check, to keep your pattern linear-looking:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
switch person1 {
| Teacher(_) => () // do nothing
| Student({reportCard: {gpa}}) if gpa < 0.5 =>
  Console.log("What's happening")
| Student(_) =>
  // fall-through, catch-all case
  Console.log("Heyo")
}
```

```js
if (person1.TAG) {
  if (person1.reportCard.gpa < 0.5) {
    console.log("What's happening");
  } else {
    console.log("Heyo");
  }
}
```

</CodeTab>

### Match on subtype variants

You can refine a variant A to variant B using the [variant type spread syntax](./variant.mdx#variant-type-spreads) in pattern matching. This is possible if variant B [is a subtype of](./variant.mdx#coercion) variant A.

Let's look at an example:

<CodeTab labels={["ReScript", "JS Output"]}>

```res
type pets = Cat | Dog
type fish = Cod | Salmon
type animals = | ...pets | ...fish

let greetPet = (pet: pets) => {
  switch pet {
  | Cat => Console.log("Hello kitty!")
  | Dog => Console.log("Woof woof doggie!")
  }
}

let greetFish = (fish: fish) => {
  switch fish {
  | Cod => Console.log("Blub blub..")
  | Salmon => Console.log("Blub blub blub blub..")
  }
}

let greetAnimal = (animal: animals) => {
  switch animal {
  | ...pets as pet => greetPet(pet)
  | ...fish as fish => greetFish(fish)
  }
}
```

```js
function greetPet(pet) {
  if (pet === "Cat") {
    console.log("Hello kitty!");
    return;
  }
  console.log("Woof woof doggie!");
}

function greetFish(fish) {
  if (fish === "Cod") {
    console.log("Blub blub..");
    return;
  }
  console.log("Blub blub blub blub..");
}

function greetAnimal(animal) {
  switch (animal) {
    case "Cat":
    case "Dog":
      return greetPet(animal);
    case "Cod":
    case "Salmon":
      return greetFish(animal);
  }
}
```

</CodeTab>

Let's break down what we did:

- Defined two different variants for pets and for fish
- Wrote a dedicated function per animal type to greet that particular type of animal
- Combined `pets` and `fish` into a main variant for `animals`
- Wrote a function that can greet any animal by _spreading_ each sub variant on its own branch, aliasing that spread to a variable, and passing that variable to the dedicated greet function for that specific type

Notice how we're able to match on parts of the main variant, as long as the variants are compatible.

The example above aliases the variant type spread to a variable so we can use it in our branch. But, you can just as easily match without aliasing if you don't care about the value:

<CodeTab labels={["ReScript", "JS Output"]}>

```res
let isPet = (animal: animals) => {
  switch animal {
  | ...pets => Console.log("A pet!")
  | _ => Console.log("Not a pet...")
  }
}

```

```js
function isPet(animal) {
  switch (animal) {
    case "Cat":
    case "Dog":
      console.log("A pet!");
      return;
    case "Cod":
    case "Salmon":
      console.log("Not a pet...");
      return;
  }
}
```

</CodeTab>

Similarily, if you want to get advanced, you can even pull out a single variant constructor. This works with and without aliases. Example:

<CodeTab labels={["ReScript", "JS Output"]}>

```res
type dog = Dog
type pets = Cat | ...dog
type fish = Cod | Salmon
type animals = | ...pets | ...fish

let isPet = (animal: animals) => {
  switch animal {
  | ...dog => Console.log("A dog!")
  | _ => Console.log("Not a dog...")
  }
}

```

```js
function isPet(animal) {
  if (animal === "Dog") {
    console.log("A dog!");
    return;
  }
  console.log("Not a dog...");
}
```

</CodeTab>

And, thanks to the rules of subtyping, the `Dog` constructor wouldn't _really_ need to be spread inside of the `pets` variant for this to work:

<CodeTab labels={["ReScript", "JS Output"]}>

```res
type pets = Cat | Dog
type fish = Cod | Salmon
type animals = | ...pets | ...fish

// Notice `dog` isn't spread into the `pets` variant,
// but this still work due to subtyping.
type dog = Dog

let isPet = (animal: animals) => {
  switch animal {
  | ...dog => Console.log("A dog!")
  | _ => Console.log("Not a dog...")
  }
}

```

```js
function isPet(animal) {
  if (animal === "Dog") {
    console.log("A dog!");
    return;
  }
  console.log("Not a dog...");
}
```

</CodeTab>

### Match on Exceptions

If the function throws an exception (covered later), you can also match on _that_, in addition to the function's normally returned values.

<CodeTab labels={["ReScript", "JS Output"]}>

```res
switch List.find(i => i === theItem, myItems) {
| item => Console.log(item)
| exception Not_found => Console.log("No such item found!")
}
```

```js
var exit = 0;

var item;

try {
  item = List.find(function (i) {
    return i === theItem;
  }, myItems);
  exit = 1;
} catch (raw_exn) {
  var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
  if (exn.RE_EXN_ID === "Not_found") {
    console.log("No such item found!");
  } else {
    throw exn;
  }
}

if (exit === 1) {
  console.log(item);
}
```

</CodeTab>

### Match on Array

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
let students = ["Jane", "Harvey", "Patrick"]
switch students {
| [] => Console.log("There are no students")
| [student1] =>
  Console.log("There's a single student here: " ++ student1)
| manyStudents =>
  // display the array of names
  Console.log2("The students are: ", manyStudents)
}
```

```js
var students = ["Jane", "Harvey", "Patrick"];

var len = students.length;

if (len !== 1) {
  if (len !== 0) {
    console.log("The students are: ", students);
  } else {
    console.log("There are no students");
  }
} else {
  var student1 = students[0];
  console.log("There's a single student here: " + student1);
}
```

</CodeTab>

### Match on List

Pattern matching on list is similar to array, but with the extra feature of extracting the tail of a list (all elements except the first one):

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
let rec printStudents = (students) => {
  switch students {
  | list{} => () // done
  | list{student} => Console.log("Last student: " ++ student)
  | list{student1, ...otherStudents} =>
    Console.log(student1)
    printStudents(otherStudents)
  }
}
printStudents(list{"Jane", "Harvey", "Patrick"})
```

```js
function printStudents(_students) {
  while (true) {
    var students = _students;
    if (!students) {
      return;
    }
    var otherStudents = students.tl;
    var student = students.hd;
    if (otherStudents) {
      console.log(student);
      _students = otherStudents;
      continue;
    }
    console.log("Last student: " + student);
    return;
  }
}

printStudents({
  hd: "Jane",
  tl: {
    hd: "Harvey",
    tl: {
      hd: "Patrick",
      tl: /* [] */ 0,
    },
  },
});
```

</CodeTab>

### Match on Dictionaries

You can pattern match on dictionaries just like you can on other ReScript data structures.

When pattern matching on a dictionary it's assumed by default that you're expecting the keys you match on to exist in the dictionary. Example:

<CodeTab labels={["ReScript", "JS Output"]}>

```res prelude
let d = dict{"A": 5, "B": 6}

// We're expecting the `B` key to exist below, and `b` will be `int` in the match branch
let b = switch d {
| dict{"B": b} => Some(b)
| _ => None
}
```

```js
let d = {
  A: 5,
  B: 6,
};

let b = d.B;

let b$1 = b !== undefined ? b : undefined;
```

</CodeTab>

However, there are situations where you want to pull out the value of a key as an option. You can do that using the `?` optional syntax in the pattern match:

<CodeTab labels={["ReScript", "JS Output"]}>

```res prelude
let d = dict{"A": 5, "B": 6}

// We're pulling out `B` regardless of if it has a value or not, and therefore get `b` as `option<int>`
let b = switch d {
| dict{"B": ?b} => b
}
```

```js
let d = {
  A: 5,
  B: 6,
};

let b = d.B;
```

</CodeTab>

Notice how in the first case, when not using `?`, we had to supply a catch-all case `_`. That's because the pattern match _expects_ `B` to exist in the first case, for the pattern to match. If `B` doesn't exist, the match falls through to the next branch, and therefore we need to catch it to be exhaustive in our matching.

However, in the second case, we don't need a catch-all case. That's because the first branch will _always_ match the dictionary - either `B` exists or it doesn't, but it doesn't matter because we're pulling it out as an optional value.

### Small Pitfall

**Note**: you can only pass literals (i.e. concrete values) as a pattern, not let-binding names or other things. The following doesn't work as expected:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
let coordinates = (10, 20, 30)
let centerY = 20
switch coordinates {
| (x, _centerY, _) => Console.log(x)
}
```

```js
var coordinates = [10, 20, 30];
var centerY = 20;

console.log(10);
```

</CodeTab>

A first time ReScript user might accidentally write that code, assuming that it's matching on `coordinates` when the second value is of the same value as `centerY`. In reality, this is interpreted as matching on coordinates and assigning the second value of the tuple to the name `centerY`, which isn't what's intended.

## Exhaustiveness Check

As if the above features aren't enough, ReScript also provides arguably the most important pattern matching feature: **compile-time check of missing patterns**.

Let's revisit one of the above examples:

<CodeTab labels={["ReScript", "JS Output"]}>

```res
let message = switch person1 {
| Teacher({name: "Mary" | "Joe"}) =>
  `Hey, still going to the party on Saturday?`
| Student({name, reportCard: {passing: true, gpa}}) =>
  `Congrats ${name}, nice GPA of ${Float.toString(gpa)} you got there!`
| Student({
    reportCard: {gpa: 0.0},
    status: Vacations(daysLeft) | Sabbatical(daysLeft)
  }) =>
  `Come back in ${Int.toString(daysLeft)} days!`
| Student({status: Sick}) =>
  `How are you feeling?`
| Student({name}) =>
  `Good luck next semester ${name}!`
}
```

```js
if (person1.TAG) {
  var match$1 = person1.status;
  var name = person1.name;
  var match$2 = person1.reportCard;
  if (match$2.passing) {
    "Congrats " +
      name +
      ", nice GPA of " +
      match$2.gpa.toString() +
      " you got there!";
  } else if (typeof match$1 === "number") {
    if (match$1 !== 0) {
      "Good luck next semester " + name + "!";
    } else {
      ("How are you feeling?");
    }
  } else if (person1.reportCard.gpa !== 0.0) {
    "Good luck next semester " + name + "!";
  } else {
    "Come back in " + match$1._0.toString() + " days!";
  }
} else {
  switch (person1.name) {
    case "Joe":
    case "Mary":
      break;
    default:
      throw {
        RE_EXN_ID: "Match_failure",
        _1: ["playground.res", 13, 0],
        Error: new Error(),
      };
  }
}
```

</CodeTab>

Did you see what we removed? This time, we've omitted the handling of the case where `person1` is `Teacher({name})` when `name` isn't Mary or Joe.

Failing to handle every scenario of a value likely constitutes the majority of program bugs out there. This happens very often when you refactor a piece of code someone else wrote. Fortunately for ReScript, the compiler will tell you so:

```
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
Some({name: ""})
```

**BAM**! You've just erased an entire category of important bugs before you even ran the code. In fact, this is how most of nullable values is handled:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
let myNullableValue = Some(5)

switch myNullableValue {
| Some(_v) => Console.log("value is present")
| None => Console.log("value is absent")
}
```

```js
var myNullableValue = 5;

if (myNullableValue !== undefined) {
  console.log("value is present");
} else {
  console.log("value is absent");
}
```

</CodeTab>

If you don't handle the `None` case, the compiler warns. No more `undefined` bugs in your code!

## Conclusion & Tips & Tricks

Hopefully you can see how pattern matching is a game changer for writing correct code, through the concise destructuring syntax, the proper conditions handling of `switch`, and the static exhaustiveness check.

Below is some advice:

Avoid using the wildcard `_` unnecessarily. Using the wildcard `_` will bypass the compiler's exhaustiveness check. Consequently, the compiler will not be able to notify you of probable errors when you add a new case to a variant. Try only using `_` against infinite possibilities, e.g. string, int, etc.

Use the `if` clause sparingly.

**Flatten your pattern-match whenever you can**. This is a real bug remover. Here's a series of examples, from worst to best:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
let optionBoolToBool = opt => {
  if opt == None {
    false
  } else if opt === Some(true) {
    true
  } else {
    false
  }
}
```

```js
function optionBoolToBool(opt) {
  if (opt === undefined) {
    return false;
  } else {
    return opt === true;
  }
}
```

</CodeTab>

Now that's just silly =). Let's turn it into pattern-matching:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
let optionBoolToBool = opt => {
  switch opt {
  | None => false
  | Some(a) => a ? true : false
  }
}
```

```js
function optionBoolToBool(opt) {
  if (opt !== undefined && opt) {
    return true;
  } else {
    return false;
  }
}
```

</CodeTab>

Slightly better, but still nested. Pattern-matching allows you to do this:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
let optionBoolToBool = opt => {
  switch opt {
  | None => false
  | Some(true) => true
  | Some(false) => false
  }
}
```

```js
function optionBoolToBool(opt) {
  if (opt !== undefined && opt) {
    return true;
  } else {
    return false;
  }
}
```

</CodeTab>

Much more linear-looking! Now, you might be tempted to do this:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
let optionBoolToBool = opt => {
  switch opt {
  | Some(true) => true
  | _ => false
  }
}
```

```js
function optionBoolToBool(opt) {
  if (opt !== undefined && opt) {
    return true;
  } else {
    return false;
  }
}
```

</CodeTab>

Which is much more concise, but kills the exhaustiveness check mentioned above; refrain from using that. This is the best:

<CodeTab labels={["ReScript", "JS Output"]}>

```res example
let optionBoolToBool = opt => {
  switch opt {
  | Some(trueOrFalse) => trueOrFalse
  | None => false
  }
}
```

```js
function optionBoolToBool(opt) {
  if (opt !== undefined) {
    return opt;
  } else {
    return false;
  }
}
```

</CodeTab>

Pretty darn hard to make a mistake in this code at this point! Whenever you'd like to use an if-else with many branches, prefer pattern matching instead. It's more concise and [performant](./variant.mdx#design-decisions) too.
